// -----------------------------------------------------------------------
//  <copyright file="LoginCommand.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2025 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2025/1/14 17:05</last-date>
// -----------------------------------------------------------------------

using DaprPlus.Identity.Application;
using DaprPlus.Identity.Domain.Entities;
using DaprPlus.Identity.Domain.Services;

using DaprPlus.Identity.Application.Identity.Users.Events;
using DaprPlus.Identity.Application.Identity.Users.Queries;

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;


namespace DaprPlus.Identity.Application.Identity.Users.Commands;

[AuthConfig(typeof(User), ResourceAccessType.Anonymous, "Login")]
public class LoginCommand : IRequest<ApiResult<UserOutDto>>, ICommand
{
    public LoginType LoginType { get; set; }

    /// <summary>
    /// 登录账号，可以是用户名，Email，手机号
    /// </summary>
    public string Account { get; set; } = null!;

    public string? Password { get; set; }

    public bool IsRemember { get; set; }

    public string? CaptchaId { get; set; }

    public string? Captcha { get; set; }

    public string? ReturnUrl { get; set; }

    public User? User { get; set; }
}

public class LoginCommandHandler(IServiceProvider provider)
    : IRequestHandler<LoginCommand, ApiResult<UserOutDto>>
{
    public async Task<ApiResult<UserOutDto>> Handle(LoginCommand request, CancellationToken cancellationToken)
    {
        var user = request.User!;
        var dto = user.MapTo<UserOutDto>();
        if (user.IsLocked)
        {
            return new ApiResult<UserOutDto>(ApiResultType.Error, "用户已被锁定，无法登录", dto);
        }

        var result = ApiResult<UserOutDto>.Success("用户登录成功", dto);
        var userManager = provider.GetRequiredService<UserManager<User>>();
        var signInManager = provider.GetRequiredService<SignInManager<User>>();

        if (request.LoginType == LoginType.Password)
        {
            if (!await userManager.HasPasswordAsync(user))
            {
                return new ApiResult<UserOutDto>(ApiResultType.Error, "当前用户未设置密码，请使用验证码方式登录", dto);
            }
            var signInResult = await signInManager.CheckPasswordSignInAsync(user, request.Password!, true);
            if (!signInResult.Succeeded)
            {
                return signInResult.ToApiResult<User, UserOutDto>(userManager, user);
            }
        }
        else
        {
            var allowed = await signInManager.CanSignInAsync(user);
            if (!allowed)
            {
                var singInResult = SignInResult.NotAllowed;
                return singInResult.ToApiResult<User, UserOutDto>(userManager, user);
            }
        }

        var loggedInDomainEvent = new UserLoggedInDomainEvent(request.LoginType, request.Account, request.IsRemember, user);
        user.AddDomainEvent(loggedInDomainEvent);

        var dbContext = provider.GetRequiredService<IDbContext>();
        await dbContext.SaveChangesAsync(cancellationToken);

        var loggedInIntegrationEvent = new UserLoggedInIntegrationEvent(request.LoginType, request.Account, request.IsRemember, user);
        //user.AddIntegrationEvent(loggedInIntegrationEvent);

        // 创建身份认证Cookie
        var httpContext = provider.GetRequiredService<IHttpContextAccessor>().HttpContext;
        if (httpContext != null)
        {
            var principalFactory = provider.GetRequiredService<IUserClaimsPrincipalFactory<User>>();
            var principal = await principalFactory.CreateAsync(user);
            await httpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal,
                new AuthenticationProperties()
                {
                    IsPersistent = request.IsRemember
                });
        }

        return result;
    }
}

public class LoginCommandValidator : AbstractValidator<LoginCommand>
{
    public LoginCommandValidator(IServiceProvider provider)
    {
        var userRepository = provider.GetRepository<User>();
        When(m => m.LoginType == LoginType.Password, () =>
        {
            RuleFor(m => m.Password).NotEmpty().WithMessage("登录密码不能为空");
            RuleFor(m => m.Account).NotEmpty().WithMessage("登录账号不能为空")
                .MustAsync(async (request, value, token) =>
                {
                    ISpecification<User> spec = value.IsEmail()
                        ? new UserByNormalizedEmailSpec<User>(value.ToUpper())
                        : value.IsMobileNumber()
                            ? new UserByPhoneNumberSpec<User>(value)
                            : new UserByNormalizedNameSpec<User>(value.ToUpper());
                    spec = spec.And(new UserIncludeRoleSpec<User>())
                        .And(new UserIncludeClaimSpec<User>());
                    var user = await userRepository.GetAsync(spec, token);
                    request.User = user;
                    return user != null;
                }).WithMessage("登录账号不存在");
        });

        When(m => m.LoginType == LoginType.ValidateCode, () =>
        {
            ClassLevelCascadeMode = CascadeMode.Stop;
            RuleLevelCascadeMode = CascadeMode.Stop;
            RuleFor(m => m.Account).NotEmpty().WithMessage("登录账号不能为空")
                .Must(value => value.IsEmail() || value.IsMobileNumber()).WithMessage("登录账号需要是邮箱或手机号");

            RuleFor(m => m.Account).MustAsync(async (request, value, token) =>
            {
                ISpecification<User> spec = value.IsEmail()
                    ? new UserByNormalizedEmailSpec<User>(value.ToUpper())
                    : new UserByPhoneNumberSpec<User>(value);
                spec = spec.And(new UserIncludeRoleSpec<User>())
                    .And(new UserIncludeClaimSpec<User>());
                var user = await userRepository.GetAsync(spec, token);
                request.User = user;
                return user != null;
            }).WithMessage("登录账号不存在");

            RuleFor(m => m.Password).NotEmpty().WithMessage("登录验证码不能为空")
                .MustAsync(async (request, value, token) =>
                {
                    var validateCodeService = provider.GetRequiredService<IValidateCodeService>();
                    var result = await validateCodeService.ValidateAsync(request.Account, ValidateCodeType.Login,
                        value!, 3, token);
                    return result.IsSuccess() ? true : throw new ValidationException(result.Message);
                });
        });
    }
}


/// <summary>
/// 表示用户登录方式的枚举
/// </summary>
public enum LoginType
{
    /// <summary>
    /// 账号、邮箱、手机号+密码登录
    /// </summary>
    Password,

    /// <summary>
    /// 邮箱、手机号+验证码登录
    /// </summary>
    ValidateCode
}



/*
名称：
作用：
需求点：

*/

